package com.illtamer.infinite.morii.service;

import com.illtamer.infinite.morii.pojo.*;
import com.illtamer.infinite.morii.util.CommonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class IndexService {

    @Resource(name = "moriiVerifyService")
    private MoriiVerifyService moriiVerifyService;

    /**
     * 查找路径下所有可用资源
     * @param uri 当前用户访问的资源文件父级路径
     *            ""、"org/"、"org/apache/commons/commons-io/1.3.2/"
     * @apiNote 版本号需遵循语义化规范
     * */
    public List<MoriiURI> getMoriiURIList(String uri) {
        final List<MoriiVerify> verifies = new LinkedList<>();
        final String[] uriPattern = uri.split("/");
        backtraceURIList(0, uriPattern, new StringBuilder(), verifies);

        if (verifies.size() == 0)
            return Collections.emptyList();
        final List<MoriiURI> uris = new LinkedList<>();
        // 文件路径
        final List<MoriiVerify> fileVerifies = verifies.stream()
                .filter(verify ->
                        uriPattern.length == CommonUtil.count(verify.getGroupId(), '.') + 3 &&
                                verify.getVersion().equals(uriPattern[uriPattern.length - 1]))
                .collect(Collectors.toList());
        fileVerifies.forEach(verify -> uris.addAll(parseMoriiFiles(verify)));
        // 文件夹路径
        final List<MoriiVerify> folderObjects = verifies.stream()
                .filter(folderVerify -> !fileVerifies.contains(folderVerify))
                .collect(Collectors.toList());
        uris.addAll(folderObjects.stream()
                .map(verify -> getNextPoint(uriPattern, verify))
                .filter(Objects::nonNull)
                .distinct() // 去重s
                .map(point -> new MoriiURI(point, false, null, -1))
                .collect(Collectors.toList()));

        return uris;
    }

    /**
     * 获取当前节点下的所有资源标识
     * @param index 起始索引值，主动调用时传 0
     * @param points 访问的资源片段数组
     * @param path 已走过的路径，主动调用时传 new StringBuilder()
     * @param verifies 用储存的查询资源的结果集
     * @return boolean 查询是否有结果
     * @apiNote 返回的资源实体类中仅具有前四个字段
     * */
    @Cacheable(value = "basic", key = "#path.toString()")
    public boolean backtraceURIList(int index, String[] points, StringBuilder path, List<MoriiVerify> verifies) {
        if (index == points.length) return false;
        if (path.length() != 0) path.append('.');
        path.append(points[index ++]);
        List<MoriiVerify> prefixVerifies = moriiVerifyService.getPrefixMoriiVerifies(path.toString());

        if (prefixVerifies.size() == 0) return false;
        // 判断当前是否为最后一级
        if (!backtraceURIList(index, points, path, verifies)) {
            // 是最后一级、检索 version
            final String formatKey = formatMoriiObjectKey(path);
            MoriiVerify moriiObject = moriiVerifyService.query(formatKey);
            if (moriiObject != null) {
                verifies.add(moriiObject);
                return true;
            }
            verifies.addAll(prefixVerifies);
        }
        return true;
    }

    private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm");

    /**
     * 返回下一个节点名称
     * @param uriPattern 访问的节点片段数组 ['', 'groupId/artifactId']
     * */
    @Nullable
    private static String getNextPoint(@NonNull String[] uriPattern, MoriiVerify verify) {
        final String[] groupIdPattern = verify.getGroupId().split("\\.");
        final String artifactId = verify.getArtifactId();
        int length = uriPattern.length, index;

        if (length == 1 && uriPattern[0].length() == 0) return groupIdPattern[0];
        if ((length -= groupIdPattern.length) > 1) return null;
        for (index = 0; index < uriPattern.length && index != groupIdPattern.length; ++ index)
            if (!uriPattern[index].equals(groupIdPattern[index])) return null;
        // index -> [1, groupIdPattern.length]
        // groupId/artifactId
        if (length == 1 && index == groupIdPattern.length)
            return artifactId.equals(uriPattern[index]) ? verify.getVersion() : null;
        // groupId
        if (index == groupIdPattern.length) return verify.getArtifactId();
        if (index != uriPattern.length) return null;
        return groupIdPattern[index];
    }

    /**
     * 将文件实例转换为展示 URI
     * */
    private List<MoriiURI> parseMoriiFiles(MoriiVerify verify) {
        List<MoriiURI> list = new ArrayList<>(4 * FileTypeEnum.values().length);
        String basicPrefix = verify.getArtifactId() + '-' + verify.getVersion();
        for (FileTypeEnum typeEnum : FileTypeEnum.values())
            list.addAll(parseMoriiFile(basicPrefix, typeEnum, verify));
        return list;
    }

    private List<MoriiURI> parseMoriiFile(String prefix, FileTypeEnum typeEnum, MoriiVerify verify) {
        String name = prefix + '.' + typeEnum.getSuffix();
        String createTime = FORMAT.format(verify.getCreateTime());
        return Arrays.asList(
                new MoriiURI(name, true, createTime, typeEnum == FileTypeEnum.JAR ? verify.getJarSize() : verify.getPomSize()),
                new MoriiURI(name + '.' + VerifyEnum.ASC.getName(), true, createTime, 833),
                new MoriiURI(name + '.' + VerifyEnum.MD5.getName(), true, createTime, 32),
                new MoriiURI(name + '.' + VerifyEnum.SHA1.getName(), true, createTime, 40)
        );
    }

    private static String formatMoriiObjectKey(StringBuilder path) {
        int count = 2;
        for (int i = path.length()-1; i >= 0; -- i)
            if (path.charAt(i) == '.') {
                if (--count == -1) break;
                path.setCharAt(i, '/');
            }
        return path.toString();
    }

}
